// dotnet $DOTNET_CSC_DLL -nologo -t:library -r:"../../Backend/System.dll" -r:"../../Backend/System.Collections.dll" -r:"../../The Scroll of Taiwu_Data/Managed/0Harmony.dll" -r:"../../Backend/mscorlib.dll" -r:"../../Backend/netstandard.dll" -r:"../../Backend/GameData.dll" -r:"../../Backend/Redzen.dll" -r:"../../The Scroll of Taiwu_Data/Managed/TaiwuModdingLib.dll" -r:"../../Backend/System.Private.CoreLib.dll" -r:"../../Backend/System.Linq.dll" -r:"../../Backend/System.Runtime.dll" -unsafe -optimize -deterministic -debug Backend.cs *.CS -out:Backend.dll
// -r:"../../Backend/System.IO.FileSystem.dll"

//! 编译方法，目前来说只能是自己改上面那行字，把缺的东西都补齐，之后送命令提示符或者用python脚本编译。
//! windows就是一坨shit……连通配符都不支持……不然我至少能写出类似 dotnet C:\Program Files\dotnet\sdk\*\Roslyn\bincore\csc.dll 这样的语法

// -r:"../../The Scroll of Taiwu_Data/Managed/Mono.Cecil.dll" -r:"../../The Scroll of Taiwu_Data/Managed/System.Core.dll"   -r:"../../The Scroll of Taiwu_Data/Managed/System.Composition.AttributedModel.dll" -r:"../../Backend/System.Runtime.dll"
/**
 *  Everyone's Unity Game Plugin
 *  Copyright (C) 2022 Neutron3529
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
//! 这是一个自带文档注释的Mod，虽然代码很shit，但里面有不少注释，或许会对之后准备玩过月逻辑的人有帮助。
//! 所有文档注释会用//!开头，请注意搜索
//! 首先是前后端，前端对后端的调用会先被塞进 ProcessMethodCall，之后分发给对应domain的CallMethod，由CallMethod进行进一步的分发。借助这一点，我们可以快速找到前端调用究竟是在使用哪个函数。
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using HarmonyLib;
using Utils;

using GongfaEffect;
using static Utils.CharacterMod;
namespace DevilNest;
[TaiwuModdingLib.Core.Plugin.PluginConfig("DevilNest","Neutron3529","0.2.0")]
public partial class DevilNest : TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin {
    public override void Initialize()=>this.HarmonyInstance = new RobustHarmonyInstance(this.GetGuid());
    public override void Dispose()=>this.HarmonyInstance.UnpatchSelf();
    public static void logger(string s)=>GameData.Utilities.AdaptableLog.Info(s);
    public static void logwarn(string s)=>GameData.Utilities.AdaptableLog.Warning("<color=#FFFF00>"+s+"</color>",true);
    public RobustHarmonyInstance HarmonyInstance;
    private bool enable(string key){
        try {
            return _enable(key);
        } catch(Exception ex){
            logwarn("enable 出错，出错键值为\""+key+"\"，错误原因是"+ex.Message);
            return false;
        }
    }
    private bool _enable(string key){
        bool enabled=false;
        if(key=="N16"){
            string str=null;
            if(GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref str)){
                if(str.Length>0) try {
                    sbyte type=0;
                    foreach (string trickstr in str.Split(',')){
                        if(Parser.Name2Id(trickstr,Config.TeammateCommand.Instance, ref type))Command.trick.Add(type);
                    }
                    return true;
                } catch (Exception e){
                    throw new Exception("训练有素字符串值有误，请修改字符串格式，或者将字符串置空\n请注意，这个错误的发生会导致“训练有素”的修改失效，但不会导致其他修改失效，可以放心使用。\n错误信息为：\n"+e.Message);
                }
            } else {
                return false;
            }
        } else if(key=="N17"){
            string str=null;
            if(GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref str)){
                if(str.Length>0) try {
                    short type=0;
                    foreach (string trickstr in str.Split(',')){
                        if(Parser.Name2Id(trickstr,Config.CharacterFeature.Instance, ref type))Feature.trick.Add(type);
                    }
                    return true;
                } catch (Exception e){
                    throw new Exception("耳濡目染字符串值有误，请修改字符串格式，或者将字符串置空\n请注意，这个错误的发生会导致“耳濡目染”的修改失效，但不会导致其他修改失效，可以放心使用。\n错误信息为：\n"+e.Message);
                }
            } else {
                return false;
            }
        }
        return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref enabled) && enabled;
    }
    static sbyte SkillQualificationLateBlooming=2;
    static int DKE_Flag=0;
    static int DeathLine=0;
    static int beautify=1;
    static int ChangeHappinessPerMonth=10;
    static int ChangeFavorabilityPerMonth=1500;
    public override void OnModSettingUpdate(){
        this.HarmonyInstance.UnpatchSelf();
        if(!GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, "ChangeHappinessPerMonth", ref ChangeHappinessPerMonth)){
            ChangeHappinessPerMonth=10;
        }
        if(!GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, "ChangeFavorabilityPerMonth", ref ChangeFavorabilityPerMonth)){
            ChangeFavorabilityPerMonth=1500;
        }
        if(enable("N01"))DKE_Flag|=1;
        if(enable("N02"))DKE_Flag|=2;
        if(enable("N03"))DKE_Flag|=4;
        if(enable("N04"))DKE_Flag|=8;
        if(enable("N05"))DKE_Flag|=16;
        if(enable("N06"))DKE_Flag|=32;
        if(enable("N07"))DKE_Flag|=64;
        if(enable("N08"))DKE_Flag|=128;
        if(enable("N09"))DKE_Flag|=256;
        if(enable("N10"))DKE_Flag|=512;
        if(enable("N11"))DKE_Flag|=1024;
        if(enable("N12"))DKE_Flag|=2048;
        if((DKE_Flag&7)>0){
            if(enable("N00")){
                try{
                    GetOffset();
                    Utils.GetOffset(); // DongLing需要使用Utils
                    this.HarmonyInstance.PatchAll(typeof(ChangeSkillQualificationLateBlooming));
                }catch(Exception e){
                    GameData.Utilities.AdaptableLog.Error("（这是Mod因为开关失效抛出的红字，不会影响游戏的正常进行与Mod其他功能的正常工作）\n    Mod用于获取存档偏移量的反序列化代码失效，因此将N00开关（调整资质到晚成的开关）无效化。\n    如果你希望将村民资质改为晚成，请到gitee与Mod作者联系。\n    如果你只是想消这个红字，请关闭N00开关");
                    GameData.Utilities.AdaptableLog.Error("错误的原因是："+e.Message);
                }
            }
            this.HarmonyInstance.PatchAll(typeof(DongLing));
        }
        if((DKE_Flag&56)>0)this.HarmonyInstance.PatchAll(typeof(KuaiLe));
        if((DKE_Flag&448)>0)this.HarmonyInstance.PatchAll(typeof(EnQing));
        if((DKE_Flag&3584)>0){
            GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, "N13", ref beautify);
            if(beautify>0){
                this.HarmonyInstance.PatchAll(typeof(Beautify));
            }
        }
        if(GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, "N14", ref DeathLine) && DeathLine>0)this.HarmonyInstance.PatchAll(typeof(Death));
        if(enable("N15"))this.HarmonyInstance.PatchAll(typeof(MustLearn));
        if(enable("N16"))this.HarmonyInstance.PatchAll(typeof(Command));
        if(enable("N17"))this.HarmonyInstance.PatchAll(typeof(Feature));
    }
    // public static uint offset_lifeSkillQualificationGrowthType=unchecked((uint)(-1));
    // public static uint offset_combatSkillQualificationGrowthType=unchecked((uint)(-1));
    public unsafe static void GetOffset(){
        EarlyCheck();
        // var character=new GameData.Domains.Character.Character();
        // var size=character.GetSerializedSize();
        // var bs=new byte[size+100];
        // FieldInfo _lifeSkillQualificationGrowthType=typeof(GameData.Domains.Character.Character).GetField("_lifeSkillQualificationGrowthType",(BindingFlags)(-1));
        // FieldInfo _combatSkillQualificationGrowthType=typeof(GameData.Domains.Character.Character).GetField("_combatSkillQualificationGrowthType",(BindingFlags)(-1));
        // _lifeSkillQualificationGrowthType.SetValue(character,(sbyte)35);
        // _combatSkillQualificationGrowthType.SetValue(character,(sbyte)29);
        // fixed(byte*pData=bs){
        //     character.Serialize(pData);
        //     int counter_35=0;
        //     int counter_29=0;
        //     for(uint i=0;i<bs.Length;i++){
        //         // logger("bit "+i+", content="+bs[i]);
        //         switch(bs[i]){
        //             case 35:offset_lifeSkillQualificationGrowthType  =i;counter_35++;if(counter_29>=2)throw new Exception("序列化结果中存在至少两个35");continue;
        //             case 29:offset_combatSkillQualificationGrowthType=i;counter_29++;if(counter_29>=2)throw new Exception("序列化结果中存在至少两个29");continue;
        //             default:continue;
        //             // default:throw new Exception("序列化结果中有不为0,35,29的项");
        //         }
        //     }
        //     if(counter_35!=1){
        //         throw new Exception("序列化结果不存在35");
        //     }
        //     if(counter_29!=1){
        //         throw new Exception("序列化结果不存在29");
        //     }
        // }
        // logger("offsets: lifeSkillQualificationGrowthType="+offset_lifeSkillQualificationGrowthType+", combatSkillQualificationGrowthType="+offset_combatSkillQualificationGrowthType);
    }
    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class Death {
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            sbyte month=GameData.Domains.DomainManager.World.GetCurrMonthInYear(); // 月份为本月初，这样被older赐福的人会立刻享受过月待遇
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag)){
                if(character.GetActualAge()>=DeathLine){
                    character.older(month,context);
                }
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class ChangeSkillQualificationLateBlooming {
        public static FieldInfo _lifeSkillQualificationGrowthType=typeof(GameData.Domains.Character.Character).GetField("_lifeSkillQualificationGrowthType",(BindingFlags)(-1));
        public static FieldInfo _combatSkillQualificationGrowthType=typeof(GameData.Domains.Character.Character).GetField("_combatSkillQualificationGrowthType",(BindingFlags)(-1));

        public static ushort FieldName2FieldId_LifeSkillQualificationGrowthType=GameData.Domains.Character.CharacterHelper.FieldName2FieldId["LifeSkillQualificationGrowthType"];
        public static ushort FieldName2FieldId_CombatSkillQualificationGrowthType=GameData.Domains.Character.CharacterHelper.FieldName2FieldId["CombatSkillQualificationGrowthType"];
        // public static MethodInfo SetModifiedAndInvalidateInfluencedCache=typeof(GameData.Domains.Character.Character).GetMethod("SetModifiedAndInvalidateInfluencedCache",(BindingFlags)(-1));
        public unsafe static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag)){
                if(character.GetLifeSkillQualificationGrowthType()!=SkillQualificationLateBlooming){
                    character.lifeSkillQualificationGrowthType(SkillQualificationLateBlooming,context);
                }
                if(character.GetCombatSkillQualificationGrowthType()!=SkillQualificationLateBlooming){
                    character.combatSkillQualificationGrowthType(SkillQualificationLateBlooming,context);
                }
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class DongLing {
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            sbyte month=GameData.Domains.DomainManager.World.GetCurrMonthInYear(); // 月份为本月初，这样被older赐福的人会立刻享受过月待遇
            sbyte month_young=(sbyte)((GameData.Domains.DomainManager.World.GetCurrMonthInYear()+11)%12); // 如此做的原因是，此时月份已经更替但人物年龄尚未更新，这里的做法是修正月份，将月份修正为“上个月月底”，这样就不会产生任何矛盾了
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag))if(DeathLine==0 || character.GetActualAge()<DeathLine){
                if(character.GetCurrAge()<16){
                    character.older(month,context);
                } else {
                    character.younger(2,month_young,context);
                }
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class KuaiLe {
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag>>3)){
                character.ChangeHappiness(context, ChangeHappinessPerMonth);
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class EnQing {
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            var taiwu=GameData.Domains.DomainManager.Taiwu.GetTaiwu();
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag>>6)){
                GameData.Domains.DomainManager.Character.ChangeFavorability(context, character, taiwu, ChangeFavorabilityPerMonth);
            }
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class Command {
        public static List<sbyte> trick=new List<sbyte>();
        public static MethodInfo SetElement_CharTeammateCommandDict = typeof(GameData.Domains.Extra.ExtraDomain).GetMethod("SetElement_CharTeammateCommandDict",(BindingFlags)(-1));
        public static FieldInfo _charTeammateCommandDict=typeof(GameData.Domains.Extra.ExtraDomain).GetField("_charTeammateCommandDict",(BindingFlags)(-1));
        public static GameData.Utilities.SByteList cmdList = GameData.Utilities.SByteList.Create();
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            var dict=(Dictionary<int, GameData.Utilities.SByteList>)_charTeammateCommandDict.GetValue(GameData.Domains.DomainManager.Extra);
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, 7)){
                if(dict.TryGetValue(character.GetId(),out cmdList) && cmdList.Items!=null && (!cmdList.Items.SequenceEqual(trick))){
                    cmdList.Items.Clear();
                    foreach(var item in trick){
                        cmdList.Items.Add(item);
                    }
                    SetElement_CharTeammateCommandDict.Invoke(GameData.Domains.DomainManager.Extra,new object[]{character.GetId(), cmdList, context});
                    logger($"完成战斗指令修改，id={character.GetId()}.");
                }
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class Feature {
        public static List<short> trick=new List<short>();
        public static void Prefix(
            GameData.Domains.Building.BuildingDomain __instance,
            GameData.Domains.Character.CharacterList ____homeless,
            Dictionary<GameData.Domains.Building.BuildingBlockKey,
            GameData.Domains.Character.CharacterList> ____comfortableHouses,
            Dictionary<int, GameData.Domains.Character.CharacterList> ____residences,
            GameData.Common.DataContext context
        ) {
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, 7)){
                var modified=0;
                var features=new HashSet<short>(character.GetFeatureIds());
                foreach(var feature in trick){
                    if(!features.Contains(feature)){
                        character.AddFeature(context, feature, true);
                        modified+=1;
                    }
                }
                logger($"完成特性添加，id={character.GetId()}, count={modified}.");
            }
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.Building.BuildingDomain),"UpdateResidentsHappinessAndFavor")]
    public static class Beautify {
        public static void Prefix(GameData.Domains.Building.BuildingDomain __instance, GameData.Domains.Character.CharacterList ____homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> ____comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> ____residences, GameData.Common.DataContext context) {
            foreach (var character in yields(____homeless, ____comfortableHouses, ____residences, DKE_Flag>>9)){
                if(character.GetCurrAge()>=16 && character.beautify(context)){
                    for(int i=1;i<beautify && character.beautify(context);i++){}
                    character.SetAvatar(character.GetAvatar(), context);
                }
            }
        }
    }

    public static IEnumerable<GameData.Domains.Character.Character> yields(GameData.Domains.Character.CharacterList homeless, Dictionary<GameData.Domains.Building.BuildingBlockKey, GameData.Domains.Character.CharacterList> comfortableHouses, Dictionary<int, GameData.Domains.Character.CharacterList> buildingResidents, int flag){
        if((flag&1)>0){
            foreach(int id in homeless.GetCollection()){
                yield return GameData.Domains.DomainManager.Character.GetElement_Objects(id);
            }
        }
        if((flag&2)>0) foreach (GameData.Domains.Character.CharacterList characterList in comfortableHouses.Values) {
            foreach(int id in characterList.GetCollection()){
                yield return GameData.Domains.DomainManager.Character.GetElement_Objects(id);
            }
        }
        if((flag&4)>0) foreach (GameData.Domains.Character.CharacterList characterList in buildingResidents.Values) {
            foreach(int id in characterList.GetCollection()){
                yield return GameData.Domains.DomainManager.Character.GetElement_Objects(id);
            }
        }
    }

    [HarmonyPatch(typeof(GameData.Domains.Character.Character),"OfflineCalcGeneralAction_RandomActions")]
    public class MustLearn {
        public static sbyte TaiwuOrg=16;
        // public static MethodInfo OfflineCalcGeneralAction_TeachSkill=typeof(GameData.Domains.Character.Character).GetMethod("OfflineCalcGeneralAction_TeachSkill",(BindingFlags)(-1));
        // public static MethodInfo OfflineCalcGeneralAction_LifeSkill=typeof(GameData.Domains.Character.Character).GetMethod("OfflineCalcGeneralAction_LifeSkill",(BindingFlags)(-1));
        public unsafe static void Prefix(GameData.Domains.Character.Character __instance, GameData.Common.DataContext context, GameData.Domains.Character.ParallelModifications.PeriAdvanceMonthGeneralActionModification mod, HashSet<int> currBlockChars, ref GameData.Domains.Character.Ai.ActionEnergySbytes ____actionEnergies){
            if(__instance.GetOrganizationInfo().OrgTemplateId==TaiwuOrg){ // 因为太吾村从创世时候就已经被创建，所以不需要担心GameData.Domains.DomainManager.Taiwu.GetTaiwuVillageLocation取不到location的问题。
                fixed(byte*ptr=____actionEnergies.Items){
                    *(ptr+0)=200; // 村民个人相关，比如疗伤，解毒
                    *(ptr+2)=200; // 研读技能书籍
                    *(ptr+4)=200; // 主动指点
                }
            }
        }
    }
}
